package com.lty.protocol.tcp;

import com.lty.protocol.tcp.handler.ServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName TcpServer
 * @Description tcp服务端启动类
 */
@Slf4j
@Component
public class TcpServer implements DisposableBean {

    private static final Integer port = 18888;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private ChannelFuture future;

    @PostConstruct
    public void init() {
        log.info("正在启动tcp服务器……");
        Map<String, String> map = new HashMap<>();
        bossGroup = new NioEventLoopGroup();// 主线程组
        workerGroup = new NioEventLoopGroup();// 工作线程组
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();// 引导对象
            bootstrap.group(bossGroup, workerGroup);// 配置工作线程组
            bootstrap.channel(NioServerSocketChannel.class);// 配置为NIO的socket通道
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                protected void initChannel(SocketChannel ch) throws Exception {// 绑定通道参数
                    ch.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
                    ch.pipeline().addLast(new ServerHandler()); // 添加自定义的处理器
                }
            });
            bootstrap.option(ChannelOption.SO_BACKLOG, 1024);// 缓冲区
            bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);// ChannelOption对象设置TCP套接字的参数，非必须步骤
            future = bootstrap.bind(port).sync();// 使用了Future来启动线程，并绑定了端口
            log.info("启动tcp服务器启动成功，正在监听端口:" + port);
            /*future.channel().closeFuture().sync();//以异步的方式关闭端口*/
        } catch (Exception e) {
            log.error("tcp协议端口打开异常，请联系管理员!" + e.getMessage());
            // 如果启动失败，应该关闭EventLoopGroup，避免资源泄露
            shutdown();
        }
    }

    @Override
    public void destroy() throws Exception {
        log.info("正在关闭tcp服务器……");
        shutdown();
    }

   /* public static void main(String[] args) {
        new TcpServer(8777).init();
    }*/

    private void shutdown() {
        if (future != null && !future.isDone()) {
            future.channel().closeFuture().syncUninterruptibly();
        }
        if (workerGroup != null) {
            workerGroup.shutdownGracefully(0, 10, TimeUnit.SECONDS);
        }
        if (bossGroup != null) {
            bossGroup.shutdownGracefully(0, 10, TimeUnit.SECONDS);
        }
    }
}
